﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="UnityJson.cs" company="MARCELOROCA">
//   Copyright (c) 2012 Marcelo Roca Urioste
//   Permission is hereby granted, free of charge, to any person obtaining a copy of this software
//   and associated documentation files (the "Software"),  to deal in the Software without restriction, 
//   including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 
//   and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, 
//   subject to the following conditions:
//   The above copyright notice and this permission notice shall be included in all copies or substantial 
//   portions of the Software.
//   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT 
//   LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 
//   IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 
//   WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 
//   SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// </copyright>
// --------------------------------------------------------------------------------------------------------------------

namespace MarceloRoca.Unity.JSon
{
    using System;
    using System.Globalization;

    using LeonardoBoshell.LitJson;

    using UnityEngine;

    /// <summary>
    /// Permite utilizar la librería Json de Leonardo Boshell con los tipos de datos de Unity
    /// </summary>
    public static class UnityJson
    {
        /// <summary>
        /// Initializes static members of the <see cref="UnityJson"/> class.
        /// </summary>
        static UnityJson()
        {
            JsonMapper.RegisterImporter<string, Bounds>(BoundsImporter);
            JsonMapper.RegisterImporter<string, Matrix4x4>(Matrix4X4Importer);
            JsonMapper.RegisterImporter<string, BoneWeight>(BoneWeightImporter);
            JsonMapper.RegisterImporter<string, Quaternion>(QuaternionImporter);
            JsonMapper.RegisterImporter<string, Vector4>(Vector4Importer);
            JsonMapper.RegisterImporter<string, Vector3>(Vector3Importer);
            JsonMapper.RegisterImporter<string, Vector2>(Vector2Importer);
            JsonMapper.RegisterImporter<string, Color>(ColorImporter);

            JsonMapper.RegisterExporter<Bounds>(BoundsExporter);
            JsonMapper.RegisterExporter<Matrix4x4>(Matrix4X4Exporter);
            JsonMapper.RegisterExporter<BoneWeight>(BoneWeightExporter);
            JsonMapper.RegisterExporter<Quaternion>(QuaternionExporter);
            JsonMapper.RegisterExporter<Vector4>(Vector4Exporter);
            JsonMapper.RegisterExporter<Vector3>(Vector3Exporter);
            JsonMapper.RegisterExporter<Vector2>(Vector2Exporter);
            JsonMapper.RegisterExporter<Color>(ColorExporter);
        }

        /// <summary>
        /// Convertir un objeto a notación json
        /// </summary>
        /// <param name="obj">
        /// El objeto a ser convertido
        /// </param>
        /// <returns>
        /// La representación del objeto en json
        /// </returns>
        public static string ToJson(object obj)
        {
            return JsonMapper.ToJson(obj);
        }

        /// <summary>
        /// Convierte un texto json a un objeto del tipo T
        /// </summary>
        /// <param name="json">
        /// El texto json
        /// </param>
        /// <typeparam name="T">
        /// El tipo de objeto al que será convertido
        /// </typeparam>
        /// <returns>
        /// El objeto del tipo T resultante
        /// </returns>
        public static T ToObject<T>(string json)
        {
            return JsonMapper.ToObject<T>(json);
        }

        #region Importers

        /// <summary>
        /// The bounds importer.
        /// </summary>
        /// <param name="data">
        /// The data.
        /// </param>
        /// <returns>
        /// The UnityEngine.Bounds.
        /// </returns>
        private static Bounds BoundsImporter(string data)
        {
            var items = data.Split(',');

            var ttt = new Bounds
                {
                    center =
                        new Vector3(
                        (float)Convert.ToDouble(items[0], NumberFormatInfo.InvariantInfo),
                        (float)Convert.ToDouble(items[1], NumberFormatInfo.InvariantInfo),
                        (float)Convert.ToDouble(items[2], NumberFormatInfo.InvariantInfo)),
                    extents = 
                        new Vector3(
                        (float)Convert.ToDouble(items[3], NumberFormatInfo.InvariantInfo),
                        (float)Convert.ToDouble(items[4], NumberFormatInfo.InvariantInfo),
                        (float)Convert.ToDouble(items[5], NumberFormatInfo.InvariantInfo))
                };

            return ttt;
        }

        /// <summary>
        /// The matrix 4 x 4 importer.
        /// </summary>
        /// <param name="data">
        /// The data.
        /// </param>
        /// <returns>
        /// The UnityEngine.Matrix4x4.
        /// </returns>
        private static Matrix4x4 Matrix4X4Importer(string data)
        {
            var items = data.Split(',');

            var ttt = new Matrix4x4
                {
                    m00 = (float)Convert.ToDouble(items[0]),
                    m01 = (float)Convert.ToDouble(items[1]),
                    m02 = (float)Convert.ToDouble(items[2]),
                    m03 = (float)Convert.ToDouble(items[3]),
                    m10 = (float)Convert.ToDouble(items[4]),
                    m11 = (float)Convert.ToDouble(items[5]),
                    m12 = (float)Convert.ToDouble(items[6]),
                    m13 = (float)Convert.ToDouble(items[7]),
                    m20 = (float)Convert.ToDouble(items[8]),
                    m21 = (float)Convert.ToDouble(items[9]),
                    m22 = (float)Convert.ToDouble(items[10]),
                    m23 = (float)Convert.ToDouble(items[11]),
                    m30 = (float)Convert.ToDouble(items[12]),
                    m31 = (float)Convert.ToDouble(items[13]),
                    m32 = (float)Convert.ToDouble(items[14]),
                    m33 = (float)Convert.ToDouble(items[15])
                };

            return ttt;
        }

        /// <summary>
        /// The bone weight importer.
        /// </summary>
        /// <param name="data">
        /// The data.
        /// </param>
        /// <returns>
        /// The UnityEngine.BoneWeight.
        /// </returns>
        private static BoneWeight BoneWeightImporter(string data)
        {
            var items = data.Split(',');

            var ttt = new BoneWeight
            {
                boneIndex0 = Convert.ToInt32(items[0]),
                boneIndex1 = Convert.ToInt32(items[1]),
                boneIndex2 = Convert.ToInt32(items[2]),
                boneIndex3 = Convert.ToInt32(items[3]),
                weight0 = (float)Convert.ToDouble(items[4]),
                weight1 = (float)Convert.ToDouble(items[5]),
                weight2 = (float)Convert.ToDouble(items[6]),
                weight3 = (float)Convert.ToDouble(items[7]),
            };

            return ttt;
        }

        /// <summary>
        /// The quaternion importer.
        /// </summary>
        /// <param name="data">
        /// The data.
        /// </param>
        /// <returns>
        /// The UnityEngine.Quaternion.
        /// </returns>
        private static Quaternion QuaternionImporter(string data)
        {
            var items = data.Split(',');

            var ttt = new Quaternion(
                (float)Convert.ToDouble(items[0], NumberFormatInfo.InvariantInfo),
                (float)Convert.ToDouble(items[1], NumberFormatInfo.InvariantInfo),
                (float)Convert.ToDouble(items[2], NumberFormatInfo.InvariantInfo),
                (float)Convert.ToDouble(items[2], NumberFormatInfo.InvariantInfo));

            return ttt;
        }

        /// <summary>
        /// The vector 4 importer.
        /// </summary>
        /// <param name="data">
        /// The data.
        /// </param>
        /// <returns>
        /// The UnityEngine.Vector4.
        /// </returns>
        private static Vector4 Vector4Importer(string data)
        {
            var items = data.Split(',');

            var ttt = new Vector4(
                (float)Convert.ToDouble(items[0], NumberFormatInfo.InvariantInfo),
                (float)Convert.ToDouble(items[1], NumberFormatInfo.InvariantInfo),
                (float)Convert.ToDouble(items[2], NumberFormatInfo.InvariantInfo),
                (float)Convert.ToDouble(items[2], NumberFormatInfo.InvariantInfo));

            return ttt;
        }

        /// <summary>
        /// The vector 3 importer.
        /// </summary>
        /// <param name="data">
        /// The data.
        /// </param>
        /// <returns>
        /// The UnityEngine.Vector3.
        /// </returns>
        private static Vector3 Vector3Importer(string data)
        {
            var items = data.Split(',');

            var ttt = new Vector3(
                (float)Convert.ToDouble(items[0], NumberFormatInfo.InvariantInfo),
                (float)Convert.ToDouble(items[1], NumberFormatInfo.InvariantInfo),
                (float)Convert.ToDouble(items[2], NumberFormatInfo.InvariantInfo));

            return ttt;
        }

        /// <summary>
        /// The vector 2 importer.
        /// </summary>
        /// <param name="data">
        /// The data.
        /// </param>
        /// <returns>
        /// The UnityEngine.Vector2.
        /// </returns>
        private static Vector2 Vector2Importer(string data)
        {
            var items = data.Split(',');

            var ttt = new Vector2(
                (float)Convert.ToDouble(items[0], NumberFormatInfo.InvariantInfo),
                (float)Convert.ToDouble(items[1], NumberFormatInfo.InvariantInfo));

            return ttt;
        }

        /// <summary>
        /// The color importer.
        /// </summary>
        /// <param name="data">
        /// The data.
        /// </param>
        /// <returns>
        /// The UnityEngine.Color.
        /// </returns>
        private static Color ColorImporter(string data)
        {
            var items = data.Split(',');

            var ttt = new Color(
                (float)Convert.ToDouble(items[0], NumberFormatInfo.InvariantInfo),
                (float)Convert.ToDouble(items[1], NumberFormatInfo.InvariantInfo),
                (float)Convert.ToDouble(items[2], NumberFormatInfo.InvariantInfo),
                (float)Convert.ToDouble(items[2], NumberFormatInfo.InvariantInfo));

            return ttt;
        }

        #endregion

        #region Exporter

        /// <summary>
        /// The bounds exporter.
        /// </summary>
        /// <param name="obj">
        /// The obj.
        /// </param>
        /// <param name="writer">
        /// The writer.
        /// </param>
        private static void BoundsExporter(Bounds obj, JsonWriter writer)
        {
            writer.Write(
                 obj.center.x.ToJsonString() + "," +
                 obj.center.y.ToJsonString() + "," +
                 obj.center.z.ToJsonString() + "," + 
                 obj.extents.x.ToJsonString() + "," +
                 obj.extents.y.ToJsonString() + "," +
                 obj.extents.z.ToJsonString() + ",");
        }

        /// <summary>
        /// The matrix 4 x 4 exporter.
        /// </summary>
        /// <param name="obj">
        /// The obj.
        /// </param>
        /// <param name="writer">
        /// The writer.
        /// </param>
        private static void Matrix4X4Exporter(Matrix4x4 obj, JsonWriter writer)
        {
            writer.Write(
                obj.m00.ToJsonString() + "," +
                obj.m01.ToJsonString() + "," +
                obj.m02.ToJsonString() + "," +
                obj.m03.ToJsonString() + "," +
                obj.m10.ToJsonString() + "," +
                obj.m11.ToJsonString() + "," +
                obj.m12.ToJsonString() + "," +
                obj.m13.ToJsonString() + "," +
                obj.m20.ToJsonString() + "," +
                obj.m21.ToJsonString() + "," +
                obj.m22.ToJsonString() + "," +
                obj.m23.ToJsonString() + "," +
                obj.m30.ToJsonString() + "," +
                obj.m31.ToJsonString() + "," +
                obj.m32.ToJsonString() + "," +
                obj.m33.ToJsonString());
        }

        /// <summary>
        /// The bone weight exporter.
        /// </summary>
        /// <param name="obj">
        /// The obj.
        /// </param>
        /// <param name="writer">
        /// The writer.
        /// </param>
        private static void BoneWeightExporter(BoneWeight obj, JsonWriter writer)
        {
            writer.Write(
                obj.boneIndex0.ToJsonString() + "," +
                obj.boneIndex1.ToJsonString() + "," +
                obj.boneIndex2.ToJsonString() + "," +
                obj.boneIndex3.ToJsonString() + "," +
                obj.weight0.ToJsonString() + "," +
                obj.weight1.ToJsonString() + "," +
                obj.weight2.ToJsonString() + "," +
                obj.weight3.ToJsonString());
        }

        /// <summary>
        /// The quaternion exporter.
        /// </summary>
        /// <param name="obj">
        /// The obj.
        /// </param>
        /// <param name="writer">
        /// The writer.
        /// </param>
        private static void QuaternionExporter(Quaternion obj, JsonWriter writer)
        {
            writer.Write(
                obj.x.ToJsonString() + "," +
                obj.y.ToJsonString() + "," +
                obj.z.ToJsonString() + "," +
                obj.w.ToJsonString());
        }

        /// <summary>
        /// The vector 4 exporter.
        /// </summary>
        /// <param name="obj">
        /// The obj.
        /// </param>
        /// <param name="writer">
        /// The writer.
        /// </param>
        private static void Vector4Exporter(Vector4 obj, JsonWriter writer)
        {
            writer.Write(
                obj.x.ToJsonString() + "," +
                obj.y.ToJsonString() + "," +
                obj.z.ToJsonString() + "," +
                obj.w.ToJsonString());
        }

        /// <summary>
        /// The vector 3 exporter.
        /// </summary>
        /// <param name="obj">
        /// The obj.
        /// </param>
        /// <param name="writer">
        /// The writer.
        /// </param>
        private static void Vector3Exporter(Vector3 obj, JsonWriter writer)
        {
            writer.Write(
                obj.x.ToJsonString() + "," +
                obj.y.ToJsonString() + "," +
                obj.z.ToJsonString());
        }

        /// <summary>
        /// The vector 2 exporter.
        /// </summary>
        /// <param name="obj">
        /// The obj.
        /// </param>
        /// <param name="writer">
        /// The writer.
        /// </param>
        private static void Vector2Exporter(Vector2 obj, JsonWriter writer)
        {
            writer.Write(
                obj.x.ToJsonString() + "," +
                obj.y.ToJsonString());
        }

        /// <summary>
        /// The color exporter.
        /// </summary>
        /// <param name="obj">
        /// The obj.
        /// </param>
        /// <param name="writer">
        /// The writer.
        /// </param>
        private static void ColorExporter(Color obj, JsonWriter writer)
        {
            writer.Write(
                obj.r.ToJsonString() + "," +
                obj.g.ToJsonString() + "," +
                obj.b.ToJsonString() + "," +
                obj.a.ToJsonString());
        }

        #endregion

        #region Privates

        /// <summary>
        /// The to json string.
        /// </summary>
        /// <param name="number">
        /// The number.
        /// </param>
        /// <returns>
        /// The System.String.
        /// </returns>
        private static string ToJsonString(this float number)
        {
            return ((double)number).ToJsonString();
        }

        /// <summary>
        /// The to json string.
        /// </summary>
        /// <param name="number">
        /// The number.
        /// </param>
        /// <returns>
        /// The System.String.
        /// </returns>
        private static string ToJsonString(this int number)
        {
            return Convert.ToString(number, NumberFormatInfo.InvariantInfo);
        }

        /// <summary>
        /// The to json string.
        /// </summary>
        /// <param name="number">
        /// The number.
        /// </param>
        /// <returns>
        /// The System.String.
        /// </returns>
        private static string ToJsonString(this double number)
        {
            var str = Convert.ToString(number, NumberFormatInfo.InvariantInfo);

            if (str.IndexOf('.') == -1 && str.IndexOf('E') == -1)
            {
                return str + ".0";
            }

            return str;
        }

        #endregion
    }
}
